// Include files.
#include "FpkException.hpp"
#include "State.hpp"

using namespace std;

/**
 *  Constructor for State.
 */
State::State() :
	m_transitions(),
	m_doneCode(StateDoneCode::GetNotDoneCode())
{
}

/**
 *  Desturctor for State.
 */
State::~State()
{
}

/**
 *  Returns the transitions map that maps a done code to a state.
 *  
 *  @returns The transition map that maps a done code to a state.
 */
const map<StateDoneCode, State*> &State::GetTransitions()
{
	return m_transitions;
}

/**
 *  Adds a transition that specifies which state to transition if the state finishes successfully.
 *  
 *  @param state The State to transition to.
 */
void State::AddTransition(State &state)
{
	AddTransition(StateDoneCode::GetSuccessCode(), state);
}

/**
 *  Adds a transition that specifies which state to transition if the state finishes with the specified DoneCode.
 *  
 *  @param doneCode The DoneCode for the transition.
 *  @param state The State to transition to.
 */
void State::AddTransition(StateDoneCode doneCode, State &state)
{
	m_transitions[doneCode] = &state;
}

/**
 *  Removes the transition with the specified StateDoneCode.
 *  
 *  @param doneCode The DoneCode of the transition.
 */
void State::RemoveTransition(StateDoneCode doneCode)
{
	m_transitions.erase(doneCode);
}


/**
 *  Gets the State's StateDoneCode.
 *  
 *  If the State is not done, it returns StateDoneCode::GetNotDoneCode().
 *  
 *  @returns The StateDoneCode.
 */
StateDoneCode State::GetDoneCode()
{
	return m_doneCode;
}

/**
 *  Gets the next State.
 *  
 *  If the State is not done, it returns itself.
 *  
 *  @returns The next State.
 */
State &State::GetNextState()
{
	if (m_doneCode.IsDone())
	{
		// TODO: Use this code for speedup once we fix all of our other problems.
		/*
		std::map<StateDoneCode, State*>::iterator it = m_transitions.find(m_doneCode);
		if (it != m_transitions.end())
			return *(it->second);
		else
		{
		*/
		
		if (m_transitions.count(m_doneCode) > 0)
			return *m_transitions[m_doneCode];
		else
		{
			if (m_doneCode.IsError())
			{
				// Try to find a generic error transition.
				if (m_transitions.count(StateDoneCode::GetGenericErrorCode()) > 0)
					return *m_transitions[StateDoneCode::GetGenericErrorCode()];
				else
					throw FpkException("State::GetNextState() - Missing generic error transition for error done code!");
			}
			else
			{
				throw FpkException("State::GetNextState() - Missing transition for non-error done code!");
			}
		}
	}
	
	// "Transitions" to itself when the state is not done.
	return *this;
}

/**
 *  Runs when the state is first run.
 */
void State::HandleActivate()
{
}

/**
 *  Periodicly runs if the state is not done yet.
 */
void State::HandlePeriodic()
{
}

/**
 *  Continuously runs if the state is not done yet.
 */
void State::HandleContinuous()
{
}

/**
 *  Sets the State's StateDoneCode, thereby finishing the State.
 *  
 *  @param doneCode The StateDoneCode.
 */
void State::SetDoneCode(StateDoneCode doneCode)
{
	m_doneCode = doneCode;
}

void State::Activate()
{
	// Reset done code. 
	SetDoneCode(StateDoneCode::GetNotDoneCode());
	
	HandleActivate();
}

void State::PeriodicInternal()
{
	// Continue if the state is not done yet.
	if (!m_doneCode.IsDone())
		HandlePeriodic();
}

void State::ContinuousInternal()
{
	// Continue if the state is not done yet.
	if (!m_doneCode.IsDone())
		HandleContinuous();
}
